﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;

namespace NetTopologySuite.Algorithm
{
	public class InteriorPointArea
	{
		private static double Avg(double a, double b)
		{
			return (a + b) / 2.0;
		}

		public InteriorPointArea(IGeometry g)
		{
			this._factory = g.Factory;
			this.Add(g);
		}

		public Coordinate InteriorPoint
		{
			get
			{
				return this._interiorPoint;
			}
		}

		private void Add(IGeometry geom)
		{
			if (geom is Polygon)
			{
				this.AddPolygon(geom);
				return;
			}
			if (geom is IGeometryCollection)
			{
				foreach (IGeometry geom2 in ((IGeometryCollection)geom).Geometries)
				{
					this.Add(geom2);
				}
			}
		}

		private void AddPolygon(IGeometry geometry)
		{
			if (geometry.IsEmpty)
			{
				return;
			}
			ILineString lineString = this.HorizontalBisector(geometry);
			double num;
			Coordinate interiorPoint;
			if (lineString.Length == 0.0)
			{
				num = 0.0;
				interiorPoint = lineString.Coordinate;
			}
			else
			{
				IGeometry geometry2 = InteriorPointArea.WidestGeometry(lineString.Intersection(geometry));
				num = geometry2.EnvelopeInternal.Width;
				interiorPoint = InteriorPointArea.Centre(geometry2.EnvelopeInternal);
			}
			if (this._interiorPoint == null || num > this._maxWidth)
			{
				this._interiorPoint = interiorPoint;
				this._maxWidth = num;
			}
		}

		private static IGeometry WidestGeometry(IGeometry geometry)
		{
			if (!(geometry is IGeometryCollection))
			{
				return geometry;
			}
			return InteriorPointArea.WidestGeometry((IGeometryCollection)geometry);
		}

		private static IGeometry WidestGeometry(IGeometryCollection gc)
		{
			if (gc.IsEmpty)
			{
				return gc;
			}
			IGeometry geometryN = gc.GetGeometryN(0);
			for (int i = 1; i < gc.NumGeometries; i++)
			{
				if (gc.GetGeometryN(i).EnvelopeInternal.Width > geometryN.EnvelopeInternal.Width)
				{
					geometryN = gc.GetGeometryN(i);
				}
			}
			return geometryN;
		}

		protected ILineString HorizontalBisector(IGeometry geometry)
		{
			Envelope envelopeInternal = geometry.EnvelopeInternal;
			double bisectorY = InteriorPointArea.SafeBisectorFinder.GetBisectorY((IPolygon)geometry);
			return this._factory.CreateLineString(new Coordinate[]
			{
				new Coordinate(envelopeInternal.MinX, bisectorY),
				new Coordinate(envelopeInternal.MaxX, bisectorY)
			});
		}

		public static Coordinate Centre(Envelope envelope)
		{
			return new Coordinate(InteriorPointArea.Avg(envelope.MinX, envelope.MaxX), InteriorPointArea.Avg(envelope.MinY, envelope.MaxY));
		}

		private readonly IGeometryFactory _factory;

		private Coordinate _interiorPoint;

		private double _maxWidth;

		private class SafeBisectorFinder
		{
			public static double GetBisectorY(IPolygon poly)
			{
				return new InteriorPointArea.SafeBisectorFinder(poly).GetBisectorY();
			}

			private SafeBisectorFinder(IPolygon poly)
			{
				this._poly = poly;
				Envelope envelopeInternal = poly.EnvelopeInternal;
				this._hiY = envelopeInternal.MaxY;
				this._loY = envelopeInternal.MinY;
				this._centreY = InteriorPointArea.Avg(this._loY, this._hiY);
			}

			private double GetBisectorY()
			{
				this.Process(this._poly.ExteriorRing);
				for (int i = 0; i < this._poly.NumInteriorRings; i++)
				{
					this.Process(this._poly.GetInteriorRingN(i));
				}
				return InteriorPointArea.Avg(this._hiY, this._loY);
			}

			private void Process(ILineString line)
			{
				ICoordinateSequence coordinateSequence = line.CoordinateSequence;
				for (int i = 0; i < coordinateSequence.Count; i++)
				{
					double y = coordinateSequence.GetY(i);
					this.UpdateInterval(y);
				}
			}

			private void UpdateInterval(double y)
			{
				if (y <= this._centreY)
				{
					if (y > this._loY)
					{
						this._loY = y;
						return;
					}
				}
				else if (y > this._centreY && y < this._hiY)
				{
					this._hiY = y;
				}
			}

			private readonly IPolygon _poly;

			private readonly double _centreY;

			private double _hiY;

			private double _loY;
		}
	}
}
